package com.lichongbing.lyoggl.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.javafaker.Address;
import com.github.javafaker.Faker;
import com.github.javafaker.Name;
import com.github.jsonzou.jmockdata.JMockData;
import com.github.jsonzou.jmockdata.MockConfig;
import com.google.common.cache.Cache;
import com.lichongbing.lyoggl.apple.AppleServe;
import com.lichongbing.lyoggl.config.ConstConfig;
import com.lichongbing.lyoggl.mapper.TimeEntriesMapper;
import com.lichongbing.lyoggl.model.LyogglUsers;
import com.lichongbing.lyoggl.model.Money;
import com.lichongbing.lyoggl.service.FlagsService;
import com.lichongbing.lyoggl.service.LyogglUsersService;
import com.lichongbing.lyoggl.service.ProjectService;
import com.lichongbing.lyoggl.service.TagsService;
import com.lichongbing.lyoggl.utils.JwtTokenUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.List;


/**
 * @author lichongbing
 * @version 1.0.0
 * @createdate 2021/12/5 11:09 上午
 * @description: TODO
 */
@Slf4j
@RestController
@RequestMapping("/users")
public class LyogglUsersController {
    @Autowired
    private LyogglUsersService lyogglUsersService;

    @Autowired
    private Faker faker;
    @Autowired
    private MockConfig mockConfig;

    @Autowired
    private Cache<String, String> cache;

    @Autowired
    private AppleServe appleServe;

    @Value("${clientID}")
    private static String clientID;
    @Value("${appleIDTokenRevokeURL}")
    private String appleIdTokenRevokeURL;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FlagsService flagsService;

    @Autowired
    private ProjectService projectService;

    @Autowired
    private TagsService tagsService;

    @Autowired
    private TimeEntriesMapper timeEntriesMapper;

    /**
     * 新增或编辑
     */
    @PostMapping("/save")
    public Object save(@RequestBody JSONObject timeEntries) {
        /**
         * 前端数据
         */
        String imageUrl = timeEntries.getString("imageUrl");
        String fullname = timeEntries.getString("fullname");
        String email = timeEntries.getString("email");
        String language = timeEntries.getString("language");
        LyogglUsers lyogglUsers = new LyogglUsers();
        lyogglUsers.setFullname(fullname);
        lyogglUsers.setImageUrl(imageUrl);
        lyogglUsers.setEmail(email);
        lyogglUsers.setLanguage(language);
        /**
         * 模拟测试数据
         */
        final Name name = faker.name();
        String username = name.username();
        String firstName = name.firstName();
        String lastName = name.lastName();
        StringBuilder newname = new StringBuilder();
        newname.append(lastName);
        newname.append(firstName);

        final Address address = faker.address();
        String country = address.country();
        // lyogglUsersService.save(lyogglUsers);
        LyogglUsers mock = JMockData.mock(LyogglUsers.class, mockConfig);
        String email1 = mock.getEmail();
        int age = mock.getAge();
        LyogglUsers mocklyogglUsers = new LyogglUsers();
        mocklyogglUsers.setEmail(email1);
        mocklyogglUsers.setAge(age);
        mocklyogglUsers.setFullname(newname.toString());
        mocklyogglUsers.setLanguage(country);
        System.out.println(mock);
        lyogglUsersService.save(mocklyogglUsers);
        return null;
    }

    /**
     * 数据分页api
     * 针对网页
     */
    @GetMapping("/v1/{pageSize}/{pagenNo}")
    public JSONArray getUsersV1(@PathVariable Integer pageSize, @PathVariable Integer pagenNo) {
        Page<LyogglUsers> page = new Page<>(pagenNo, pageSize);
        Page<LyogglUsers> result = lyogglUsersService.selectPage(page, Wrappers.<LyogglUsers>lambdaQuery().ge(LyogglUsers::getAge, 1).orderByAsc(LyogglUsers::getAge));
        List<LyogglUsers> records = result.getRecords();
        String jsonString = JSON.toJSONString(records);
        JSONArray jsonArray = JSONArray.parseArray(jsonString);
        return jsonArray;
    }

    /**
     * 针对移动端api
     *
     * @param jsonObject
     * @return
     */
    @PostMapping("/v1")
    public JSONArray getUsersV1(@RequestBody JSONObject jsonObject) {
        Integer pageSize = Integer.valueOf(jsonObject.getString("pageSize"));
        Integer pagenNo = Integer.valueOf(jsonObject.getString("pagenNo"));
        Page<LyogglUsers> page = new Page<>(pagenNo, pageSize);
        Page<LyogglUsers> result = lyogglUsersService.selectPage(page, Wrappers.<LyogglUsers>lambdaQuery().ge(LyogglUsers::getAge, 1).orderByAsc(LyogglUsers::getAge));
        List<LyogglUsers> records = result.getRecords();
        String jsonString = JSON.toJSONString(records);
        JSONArray jsonArray = JSONArray.parseArray(jsonString);
        return jsonArray;

    }

    /**
     * sign with in apple
     *
     * @param jsonObject
     * @param response
     * @return
     */
    @PostMapping("registerlogin")
    public String registerOrLogin(@RequestBody JSONObject jsonObject, HttpServletResponse response) {
        //获取apple的用户唯一标识
        System.out.println(jsonObject.toJSONString());
        String userid = jsonObject.getString("user");
        //authorizationCode传递给苹果服务做校验 5分钟有效
        String authorizationCode = jsonObject.getString("authorizationCode");
        //获取apple的identityToken 10 分钟有效
        String identityToken = jsonObject.getString("identityToken");
        //获取设备deviceToken
        String deviceToken = jsonObject.getString("deviceToken");
        String expire1 = jsonObject.getString("expire");
        //解码 apple的identityToken
        identityToken = new String(Base64.decodeBase64(identityToken));
        //验证客户端identityToken请求
        Boolean isok = this.checkIdentityToken(identityToken);
        // 查询用户是否存在
        LyogglUsers lyogglUsers = null;
        lyogglUsers = lyogglUsersService.getLyogglUsersById(userid);
        try {
            if (isok) {
                if(expire1==null){
                    String refreshToken = appleServe.generateToken(authorizationCode);
                    String token = JwtTokenUtils.createTokens(userid, 604800);
                    if (lyogglUsers == null) {
                        LyogglUsers lyogglUsers1 = new LyogglUsers();
                        lyogglUsers1.setId(userid);
                        lyogglUsers1.setApiToken(token);
                        lyogglUsers1.setIstoken("1");
                        lyogglUsers1.setRefreshToken(refreshToken);
                        lyogglUsers1.setDeviceToken(deviceToken);
                        lyogglUsersService.save(lyogglUsers1);
                    }else{
                        // 更新refreshToken
                        lyogglUsers.setRefreshToken(refreshToken);
                        // 更新deviceToken
                        lyogglUsers.setDeviceToken(deviceToken);
                        lyogglUsersService.updateById(lyogglUsers);
                        this.updateToken(userid,token);
                    }
                    response.setHeader("token", token);
                    return "登录成功";

                }else  {
                    long expire = Long.parseLong(jsonObject.getString("expire"));
                    String token = JwtTokenUtils.createTokens(userid, expire);
                    if(lyogglUsers ==null){
                        LyogglUsers lyogglUsers1 = new LyogglUsers();
                        lyogglUsers1.setId(userid);
                        lyogglUsers1.setApiToken(token);
                        lyogglUsers1.setIstoken("1");
                        lyogglUsersService.save(lyogglUsers1);
                    }
                    response.setHeader("token", token);
                    return "登录成功";
                }
            } else {
                return "登录失败";
            }
        } catch (Exception e) {
            return "系统错误";
        }
    }

    /**
     * 缓存token
     * @param userid
     * @param token
     * @return
     */
    private boolean updateToken(String userid, String token){
        UpdateWrapper<LyogglUsers> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", userid).set("api_token", token).set("istoken", "1");
        boolean update = lyogglUsersService.update(null, updateWrapper);
        return update;
    }

    /**
     * 验证客户端identityToken请求是否有效
     *
     * @param identityToken
     * @return
     */
    private Boolean checkIdentityToken(String identityToken) {
        boolean result = false;
        try {
            //分段切割获取数据
            String firstDate = new String(Base64.decodeBase64(identityToken.split("\\.")[0]), "UTF-8");
            System.out.println(firstDate);
            //第二段数据
            String claim = new String(Base64.decodeBase64(identityToken.split("\\.")[1]), "UTF-8");
            // 获取aud
            String aud = JSONObject.parseObject(claim).get("aud").toString();
            // 获取sub
            String sub = JSONObject.parseObject(claim).get("sub").toString();
            // 获取 email
           // String email = JSONObject.parseObject(claim).get("email").toString();
            // 获取iss
            String iss = JSONObject.parseObject(claim).get("iss").toString();
            // 获取kid
            String kid = JSONObject.parseObject(firstDate).get("kid").toString();
            //通过kid 获取 apple 公钥
            PublicKey publicKey = processPublicKey(kid);
            // 通过 apple 的公钥 解密签名
            JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
            jwtParser.requireIssuer(iss);
            jwtParser.requireAudience(aud);
            jwtParser.requireSubject(sub);
            //验证客户端请求
            Jws<Claims> claims = jwtParser.parseClaimsJws(identityToken);
            if (claims != null && claims.getBody().containsKey("auth_time")) {
                result = true;
            }
        } catch (Exception e) {
            System.out.println(e);
        }
        return result;
    }

    private JSONObject getApplePublicKey(@PathVariable String kid) {
        String ifPresent = cache.getIfPresent("keys");
        JSONArray jsonArray = JSONArray.parseArray(ifPresent);
        String kidValue = null;
        JSONObject jsonObject = null;
        int i = 0;
        while (!kid.equals(kidValue)) {
            jsonObject = jsonArray.getJSONObject(i);
            kidValue = jsonObject.getString("kid");
            i++;
        }

        return jsonObject;
    }

    /**
     * 获取apple公钥
     *
     * @param kid
     * @return PublicKey
     */
    private PublicKey processPublicKey(String kid) {
        String exponent = getApplePublicKey(kid).getString("e");
        String modulus = getApplePublicKey(kid).getString("n");
        String kty = getApplePublicKey(kid).getString("kty");
        Assert.hasLength(exponent, "publicKey中e值为空");
        Assert.hasLength(modulus, "publicKey中n值为空");
        RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(
                new BigInteger(1, Base64.decodeBase64(modulus)),
                new BigInteger(1, Base64.decodeBase64(exponent)));
        KeyFactory keyFactory = null;
        PublicKey fianlKey = null;
        try {
            keyFactory = KeyFactory.getInstance(kty);
            fianlKey = keyFactory.generatePublic(rsaPublicKeySpec);
        } catch (NoSuchAlgorithmException e) {
            log.error("AppleID processPublicKey NoSuchAlgorithmException{}", e.getMessage());
        } catch (InvalidKeySpecException e) {
            log.error("AppleID processPublicKey InvalidKeySpecException{}", e.getMessage());
        }
        Assert.notNull(fianlKey, "AppleID processPublicKey pulicKey is null");
        return fianlKey;
    }

    /**
     * appleId 账户删除注销
     * @param request
     * @return
     */
    @PostMapping("/revoke")
    public String appleIdRevoke(HttpServletRequest request) {
        try {
            String token = request.getHeader("token");
            String userid = JwtTokenUtils.getUserid(token);
            boolean flag = lyogglUsersService.isToken(userid);
            if(flag){
                LyogglUsers lyogglUsersById = lyogglUsersService.getLyogglUsersById(userid);
                String refresh_token = lyogglUsersById.getRefreshToken();
                MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
                map.add("client_id", ConstConfig.CLIENT_ID);
                map.add("client_secret", AppleServe.generateClientSecret());
                map.add("token_type_hint", "refresh_token");
                map.add("token", refresh_token);
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                HttpEntity<MultiValueMap<String, String>> req = new HttpEntity<>(map, headers);
                ResponseEntity<String> values = restTemplate.postForEntity(appleIdTokenRevokeURL, req, String.class);
                //Apple官网上请求只有200与400,这里没有抛异常,证明是成功注销的
                log.info("注销token成功");
                UpdateWrapper<LyogglUsers> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("id", userid).set("istoken", "0");
                boolean update = lyogglUsersService.update(null, updateWrapper);
                //删除账户
               // lyogglUsersService.removeById(lyogglUsersById);
                return "注销token成功";
            }else{
                return "注销token失败";
            }

        } catch (Exception e) {
            return "null";
        }
    }

    /**
     * 退出登录
     * @param request
     * @return
     */
    @PostMapping("/loginout")
    public String loginout(HttpServletRequest request){
        String token = request.getHeader("token");
        String userid = JwtTokenUtils.getUserid(token);
        boolean flag = lyogglUsersService.isToken(userid);
        boolean expiration = JwtTokenUtils.isExpiration(token);
        if (expiration == false) {
            if(flag) {
                UpdateWrapper<LyogglUsers> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("id", userid).set("istoken", "0");
                boolean update = lyogglUsersService.update(null, updateWrapper);
                return "退出登录成功";
            }else {
                return "退出登录失败";
            }
        }else {
            return "token过期";
        }

    }

    /**
     * 更新pushToken
     * @param jsonObject
     * @param request
     * @return
     */
    @PostMapping("updatePushToken")
    public String saveDynamicIsland(@RequestBody JSONObject jsonObject,HttpServletRequest request){
        String pushToken = jsonObject.getString("pushToken");
        String token = request.getHeader("token");
        String userid = JwtTokenUtils.getUserid(token);
        boolean expiration = JwtTokenUtils.isExpiration(token);
        if (expiration == false) {
                UpdateWrapper<LyogglUsers> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("id", userid).set("push_token", pushToken);
                boolean update = lyogglUsersService.update(null, updateWrapper);
             return "ok"+update;
        }else {
            return "token过期";
        }

    }


}
